// Implementation of a styled button for Gtk+
// Copyright 2006 (C) Ralph Thomas

#include <view/styled_button.h>
#include <binder.hpp>
#include <gtk/gtk.h>
#include <gtk/os_utilities.hpp>
#include <view/gtk/layoutable_widget.h>

namespace view {
	//
	/// Contains the button widget and connects it's events up.
	//
	class button_t {
		typedef adobe::implementation::signal_wrapper_zero_t<void> clicked_t;
		adobe::auto_ptr<GtkWidget>	control_m;
		clicked_t			clicked_m;
		adobe::binder_ptr_t		binder_m;
		void update_active( bool active ) {
			gtk_widget_set_sensitive( control_m.get(), active );
		}
	  public:
		button_t( const adobe::binder_ptr_t& action, const std::string& name, bool is_stock, const std::string& style, bool icon_only ) : binder_m( action ) {
			//
			// Build our widget and connect it up to the binder's invoke action.
			//
			if( icon_only ) {
				control_m.reset( gtk_button_new() );
				GtkWidget* stock_image( gtk_image_new_from_stock( name.c_str(), GTK_ICON_SIZE_BUTTON ) );
				gtk_container_add( GTK_CONTAINER( control_m.get() ), stock_image );
				gtk_widget_show( stock_image );
			} else {
				if( is_stock )
					control_m.reset( gtk_button_new_from_stock( name.c_str() ) );
				else	control_m.reset( gtk_button_new_with_label( name.c_str() ) );
			}
			//
			// Connect it.
			//
			clicked_m.set_callback(
				boost::bind( &adobe::binder_t::invoke, binder_m ) );
			clicked_m.connect( control_m.get(), "clicked" );
			binder_m->set_signals( 0, boost::bind( &button_t::update_active, this, _1 ) );
			//
			// Use a custom style if we have one.
			//
			if( !style.empty() )
				gtk_widget_set_name( control_m.get(), style.c_str() );
		}
		GtkWidget* widget() const { return control_m.get(); }
	};
	adobe::widget_node_t styled_button_factory(
		const adobe::dictionary_t&	parameters,
		const adobe::widget_node_t&	parent,
		const adobe::factory_token_t&	token )
	{
		//
		// The button should have an action.
		//
		adobe::binder_ptr_t action =
			parameters.at( adobe::static_name_t( "action" ) ).cast<adobe::binder_ptr_t>();
		//
		// Get the name or stock id.
		// 
		std::string name;
		bool is_stock( false );
		bool icon_only( false );
		if( parameters.count( adobe::static_name_t( "name" ) ) )
			name = parameters.at( adobe::static_name_t( "name" ) ).cast<std::string>();
		else {
			name = parameters.at( adobe::static_name_t( "stock" ) ).cast<std::string>();
			is_stock = true;
		}
		adobe::dictionary_t::const_iterator i( parameters.find( adobe::static_name_t( "icon_only" ) ) );
		if( i != parameters.end() ) i->second.cast( icon_only );
		if( icon_only && !is_stock )
			std::cerr << "styled_button: icon_only should only be used with stock buttons" << std::endl;
		//
		// See if we have a custom style.
		//
		std::string style;
		if( parameters.count( adobe::static_name_t( "style" ) ) )
			style = parameters.at( adobe::static_name_t( "style" ) ).cast<std::string>();
		//
		// Make the button.
		//
		button_t* button = new button_t( action, name, is_stock, style, icon_only );
		token.client_holder_m.assemblage_m.delete_on_destruction( button );
		//
		// Insert this widget into it's parent.
		//
		GtkWidget* w = button->widget();
		token.display_m.insert( parent.display_token_m, w );
		//
		// Bind the calculate and place signals for Eve.
		//
		adobe::any_placeable_t* p(new adobe::any_placeable_t(layoutable_widget_t(w)));
		token.client_holder_m.assemblage_m.delete_on_destruction(p);
		//
		// Register the widget with Eve.
		//
		adobe::eve_t::iterator eve_token(
			token.client_holder_m.eve_m.add_view_element( parent.eve_token_m,
				adobe::layout_attributes_t(),
				false,
				parameters,
				boost::ref(*p) )
			);
		//
		// Return the new button.
		//
		return adobe::widget_node_t( parent.size_m, eve_token, w, parent.keyboard_token_m );

	}
};
